/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: dog_service.cpp
 *
 * Purpose: service model(interface) define for puppy dog
 *
 * Developer:
 *   wen.gu , 2020-08-20
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#define LOG_TAG "_SRV"
#include "service/dog_service.h"
#include "service/dog_communication_service_port.h"

#include "dog_plugin_manager.h"
#include "dog_log.h"

namespace dog
{

/******************************************************************************
 **    MACROS
 ******************************************************************************/

/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/

class Service::impl
{
public:
    bool mLogTrace = false;
    bool mIsInitialized = false;
    bool mIsRunning = false;
    std::string mSrvName = "";
    std::string mComType = "";
    CommunicationServicePort* mPort = nullptr;
    ServiceMethodMap mSrvMethods;
public:
    void setupPort();
    DogErrc doMethod_l(const std::string& methodName, const Value& param, Value& reply);
};

/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/
void Service::impl::setupPort()
{
    mPort->setObserver([this](const char* methodName, const Value& param, Value& reply)
    {
        doMethod_l(methodName, param, reply);
    });
}

DogErrc Service::impl::doMethod_l(const std::string& methodName, const Value& param, Value& reply)
{
    if (methodName.empty()) return DogErrc::BadParameter;

    if (mLogTrace)
    {
        std::string jsonStr = "";
        JsonWriteRaw(param, jsonStr);
        LOGD("invoke method[%s], param: %s\n", methodName.c_str(), jsonStr.c_str());
    }

    DogErrc ret = DogErrc::NotImplemented;
    ServiceMethodMap::iterator it = mSrvMethods.find(methodName);

    if (it != mSrvMethods.end())
    {
        ServiceMethod func = it->second;

        if (func)
        {
            ret = func(param, reply);
        }
    }

    Error2Json(ret, reply);

    if (mLogTrace)
    {
        std::string jsonStr = "";
        JsonWriteRaw(reply, jsonStr);

        LOGD("Invoke method[%s], return: %s\n", methodName.c_str(), jsonStr.c_str());
    }

    return ret;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
Service::Service()
    :mImpl(new impl)
{
    /** todo, something */
}

Service::~Service()
{
    /** todo, something */
    if (mImpl->mPort)
    {
        mImpl->mPort->stopService();
    }

    stop();
    if (mImpl->mPort)
    {
        delete mImpl->mPort;
    }
}


/**
 *  [in]  serviceName: the name of service
 *  [in]  comType:  the type of communication(e.g. dbus, cm an so on)
 */
DogErrc Service::initialize(ConstString& serviceName, ConstString& comType)
{
    LOGD("===>\n");
    PluginManager& pm = PluginManager::getInstance();
    ComSrvPortCreateParam param;

    param.srvName = serviceName;
    param.comType = comType;
    DogErrc ret = pm.find(&param, (Component**)&mImpl->mPort);

    if (ret == DogErrc::OK)
    {
        mImpl->setupPort();
        mImpl->mIsInitialized = true;
        LOGD("load communication service port component(%s) success\n", mImpl->mPort->name().c_str());
        /**todo something */
        return DogErrc::OK;
    }
    else
    {
        LOGE("create communication component(%s) failed(%d, %s)\n", comType.c_str(), ret, ErrorStr(ret));
    }
    LOGD("===>\n");
    return DogErrc::Undefined;
}

DogErrc Service::start()
{
    if (!mImpl->mIsInitialized) return DogErrc::NoInit;

    if (mImpl->mIsRunning) return DogErrc::InvalidStatus;

    mImpl->mIsRunning = mImpl->mPort->offerService();

    return (mImpl->mIsRunning ? DogErrc::OK : DogErrc::Undefined);
}

DogErrc Service::stop()
{
    if (!mImpl->mIsInitialized) return DogErrc::NoInit;
    if (!mImpl->mIsRunning) return DogErrc::InvalidStatus;

    mImpl->mIsRunning = !mImpl->mPort->stopService();

    return  ((!mImpl->mIsRunning) ? DogErrc::OK : DogErrc::Undefined);
}

void Service::enableLogTrace(bool enable)
{
    mImpl->mLogTrace = enable;
}

bool Service::isRunning()
{
    return mImpl->mIsRunning;
}

DogErrc Service::doMethod(const std::string& methodName, const Value& param, Value& reply)  /** do method call */
{
    return mImpl->doMethod_l(methodName, param, reply);
}

DogErrc Service::emitSignal(const std::string& sigName, const Value& value)
{
    if (mImpl->mPort)
    {
        if (mImpl->mLogTrace)
        {
            std::string jsonStr = "";
            JsonWriteRaw(value, jsonStr);
            LOGD("Emit signal(name[%s], value[%s])\n", sigName.c_str(), jsonStr.c_str());
        }

        mImpl->mPort->emitSignal(sigName.c_str(), value);
        return DogErrc::OK;
    }

    return DogErrc::NoInit;
}


DogErrc Service::registerMethod(const std::string& methodName, ServiceMethod method)
{
    if (methodName.empty() || (method == nullptr)) return DogErrc::BadParameter;

    ServiceMethodMap::iterator it = mImpl->mSrvMethods.find(methodName);

    if (it != mImpl->mSrvMethods.end())
    {
        mImpl->mSrvMethods[methodName] = method;
    }

    return DogErrc::AlreadyExisted;
}

ServiceMethodMap& Service::getMethodMap()
{
    return mImpl->mSrvMethods;
}

} /** namespace dog */
